﻿#ifndef __INC_QGDRV_D11_RDH_H__
#define __INC_QGDRV_D11_RDH_H__

#ifdef _MSC_VER
#pragma once
#endif

#include "qgp/qgach.h"
#include <d3d11.h>
#include <d3d11sdklayers.h>
#include <d3dcompiler.h>
#include "qg_nfo.h"
#include "qg_animtex.h"

//////////////////////////////////////////////////////////////////////////
// DIRECTX11 
#if 0
#define D11SURFACEFORMAT		DXGI_FORMAT_R8G8B8A8_UNORM
#else
#define D11SURFACEFORMAT		DXGI_FORMAT_B8G8R8A8_UNORM
#endif


//////////////////////////////////////////////////////////////////////////
// 래스터라이저
class D11Rasz : public qgRasz
{
	KCH_AUTOCLASS(D11Rasz, qgRasz, "D11Rasz");
	KT_DECL_TYPE_INIT(D11Rasz);
	KCH_FRIEND(D11Rdh);

private:
	D11Rasz() {};
	~D11Rasz();
};


//////////////////////////////////////////////////////////////////////////
// 블렌드
class D11Blnd : public qgBlnd
{
	KCH_AUTOCLASS(D11Blnd, qgBlnd, "D11Blnd");
	KT_DECL_TYPE_INIT(D11Blnd);
	KCH_FRIEND(D11Rdh);

private:
	bool				_independ;

private:
	D11Blnd() {};
	~D11Blnd();
};


//////////////////////////////////////////////////////////////////////////
// 샘플러
class D11Splr : public qgSplr
{
	KCH_AUTOCLASS(D11Splr, qgSplr, "D11Splr");
	KT_DECL_TYPE_INIT(D11Splr);
	KCH_FRIEND(D11Rdh);

private:
	D11Splr() {};
	~D11Splr();
};


//////////////////////////////////////////////////////////////////////////
// 뎁스 스텐실
class D11Dhsl : public qgDhsl
{
	KCH_AUTOCLASS(D11Dhsl, qgDhsl, "D11Dhsl");
	KT_DECL_TYPE_INIT(D11Dhsl);
	KCH_FRIEND(D11Rdh);

private:
	bool				_iskeep;
	bool				_iswrite;

private:
	D11Dhsl() {};
	~D11Dhsl();
};


//////////////////////////////////////////////////////////////////////////
// 세이더
class D11Shader : public qgShader
{
	KCH_AUTOCLASS(D11Shader, qgShader, "D11Shader");
	KT_DECL_TYPE_INIT(D11Shader);
	KCH_FRIEND(D11Rdh);
	KCH_FRIEND(D11Layout);
	KCH_INTERN(InclImpl);

public:
	struct MapBuffer : public tpl::MemAlloc < >
	{
		tpl::RefPtr<ID3D11Buffer>	buf;
		D3D11_SHADER_BUFFER_DESC	desc;
		tpl::vector<qbShaderVar>	vars;
	};

	struct BufferData : public tpl::MemAlloc < >
	{
		kint								ref;
		tpl::RefPtr<ID3D11ShaderReflection>	sr;
		tpl::vector<MapBuffer*>				maps;
		tpl::vector<kbyte>					keep;
	};

	typedef tpl::BlueStr<64> Key;
	typedef tpl::vector<tpl::string> DefArray;

private:
	tpl::RefPtr<ID3D11PixelShader>	_ps;
	tpl::RefPtr<ID3D11VertexShader>	_vs;

	BufferData*			_bd_ps;
	BufferData*			_bd_vs;

	bool				_linked;

	Key					_key;
	DefArray			_defs;

private:
	D11Shader() throw() {};
	~D11Shader() throw();

public:
	K_OVR bool BindFile(qgShaderType type, const char* filename, kint flags);
	K_OVR bool BindData(qgShaderType type, kconstpointer data, kint size, kint flags);
	K_OVR bool BindShader(qgShaderType type, qgShader* shd);
	K_OVR bool BindShader(qgShaderType type, const char* name);

	K_OVR void AddCondition(const char* str);
	K_OVR void ClearConditions();

	K_OVR bool Link();

	//
	void _ProcessMap(ID3D11DeviceContext* ctx) throw();

private:
	template <ksize_t COUNT>
	UINT _UploadShader(BufferData* bd, ID3D11Buffer* (&bufs)[COUNT], ID3D11DeviceContext* ctx) throw();

	BufferData* _AnalyzeData(qgShaderType type, kpointer data, ksize_t size, bool keep, bool parseinput);
	bool _BindData(qgShaderType type, kconstpointer data, kint size, kint flags, const char* signature) throw();

	void _OutputError(ID3DBlob* errs, kint errcode, const char* target);
	bool _CreatePixelShader(kconstpointer data, kint size, D3D_SHADER_MACRO* mcs, InclImpl& inc, DWORD dw, const char* signature) throw();
	bool _CreateVertexShader(kconstpointer data, kint size, D3D_SHADER_MACRO* mcs, InclImpl& inc, DWORD dw, const char* signature) throw();

	static D3D_SHADER_MACRO* _MakeDefinition(const DefArray& defs);
	static DWORD _MakeFlags(kint v);
	template <typename T>
	static void _RemoveData(tpl::RefPtr<T>& ifp, BufferData*& bd, bool reset);

	static void _AutoInitFunc();
	static kpointer _AutoMatchFunc(qbShaderVar& v);

	static qgShaderData _TypeToData(const D3D11_SHADER_TYPE_DESC& std);

private:
	static kconstpointer BaseKey(kNodeBase* ptr);
	static bool BaseEq(kNodeBase* ptr, const Key& k2);
	static ksize_t KeyHash(const Key& key);
};


//////////////////////////////////////////////////////////////////////////
// 레이아웃
class D11Layout : public qgVertexLayout
{
	KCH_AUTOCLASS(D11Layout, qgVertexLayout, "D11Layout");
	KT_DECL_TYPE_INIT(D11Layout);
	KCH_FRIEND(D11Rdh);
	KCH_FRIEND(D11Shader);

public:
	struct Key
	{
		std::array<D3D11_INPUT_ELEMENT_DESC, QGSGVERTEXLAYOUT_MAX_VALUE> list;
		ksize_t hash;
		ksize_t used;
	};

private:
	Key					_key;

private:
	D11Layout() throw() {};
	~D11Layout() throw();

private:
	bool _BakeShader(D11Shader* shd, ID3D11InputLayout** ret, D11Rdh* dev);

private:
	static kconstpointer BaseKey(kNodeBase* ptr);
	static bool BaseEq(kNodeBase* ptr, const Key& k2);
	static ksize_t KeyHash(const Key& key);
};


//////////////////////////////////////////////////////////////////////////
// 버퍼
class D11Buffer : public qgBuffer
{
	KCH_AUTOCLASS(D11Buffer, qgBuffer, "D11Buffer");
	KCH_FRIEND(D11Rdh);

private:
	D3D11_BUFFER_DESC	_bd;

	kpointer			_lock;
	kint				_flags;

	ID3D11Buffer*		_stag;
	bool				_upload;	// 스태깅 업로드해야하는가?

private:
	D11Buffer() throw() {};
	~D11Buffer() throw();

public:
	K_OVR kpointer Map(kint flags);
	K_OVR bool Unmap();
};


//////////////////////////////////////////////////////////////////////////
// 뎁스 버퍼
class D11DepthBuffer : public qgDepthBuffer
{
	KCH_AUTOCLASS(D11DepthBuffer, qgDepthBuffer, "D11DepthBuffer");
	KT_DECL_TYPE_INIT(D11DepthBuffer);
	KCH_FRIEND(D11Rdh);

protected:
	D11DepthBuffer() throw() {};
	~D11DepthBuffer() throw();

private:
	static kconstpointer BaseKey(kNodeBase* ptr);
	static bool BaseEq(kNodeBase* ptr, kconstpointer k2);
	static ksize_t KeyHash(kconstpointer key);
	static ksize_t MakeKey(kuint width, kuint height);
};


//////////////////////////////////////////////////////////////////////////
// 텍스쳐 기본
class D11TextureBase : public qgTexture
{
	KCH_AUTOCLASS_ABSTRACT(D11TextureBase, qgTexture, "D11TextureBase");
	KT_DECL_TYPE_INIT(D11TextureBase);
	KCH_FRIEND(D11Rdh);

public:
	typedef char* Key;

protected:
	DXGI_FORMAT			_dev_fmt;
	DXGI_SAMPLE_DESC	_dev_sdc;

	ID3D11Resource*		_dev_tex;

	Key					_key;

protected:
	D11TextureBase() throw() {};
	~D11TextureBase() throw();

public:
	ID3D11ShaderResourceView* GetShaderResourceView() { return CastDescription<ID3D11ShaderResourceView>(); }

protected:
	bool _InitDimension(kuint maxsize, kuint width, kuint height, kuint depth, kuint mips, qgColorFormat fmt);
	bool _InitProperty(D11Rdh* rdh, const char* name, qgTextureType type, kint flags);

private:
	static kconstpointer BaseKey(kNodeBase* ptr);
	static bool BaseEq(kNodeBase* ptr, const Key& k2);
	static ksize_t KeyHash(const Key& key);
};


//////////////////////////////////////////////////////////////////////////
// 텍스쳐2
class D11Texture2 : public D11TextureBase
{
	KCH_AUTOCLASS(D11Texture2, D11TextureBase, "D11Texture2");
	KCH_FRIEND(D11Rdh);

protected:
	qgImage*			_img_raw;
	qgImage*			_img_mip;

	kint				_lock_mip;
	kint				_lock_slice;

protected:
	D11Texture2() throw();
	~D11Texture2() throw();

public:
	K_OVR bool Map(qbTextureMap* map, kint flag, kint mips, kint sliceface);
	K_OVR bool Unmap();

protected:
	bool _Create(D11Rdh* rdh, bool iscube, const D3D11_SUBRESOURCE_DATA* subres);
	bool _Create(D11Rdh* rdh, qgImage* img);
};


//////////////////////////////////////////////////////////////////////////
// 텍스쳐3
class D11Texture3 : public D11TextureBase
{
	KCH_AUTOCLASS(D11Texture3, D11TextureBase, "D11Texture3");
	KCH_FRIEND(D11Rdh);

protected:
	D11Texture3() throw();
	~D11Texture3() throw();

public:
	K_OVR bool Map(qbTextureMap* map, kint flag, kint mips, kint sliceface);
	K_OVR bool Unmap();

protected:
	bool _Create(D11Rdh* rdh);
};


//////////////////////////////////////////////////////////////////////////
// 모션 텍스쳐, 클래스의 이중 상속이 있다.
class D11AnimTexture : public D11TextureBase, public qbServAnimTexture
{
	KCH_AUTOCLASS(D11AnimTexture, D11TextureBase, "D11AnimTexture");
	KCH_FRIEND(D11Rdh);

private:
	D11AnimTexture() throw() {};
	~D11AnimTexture() throw();

public:
	K_OVR bool Map(qbTextureMap* map, kint flag, kint mips, kint sliceface) { return false; }
	K_OVR bool Unmap() { return false; }

	K_OVR qbAnimTexture* GetMotion() { return this; }

	K_OVR bool MotionUpdate(float adv);

private:
	bool _CreateAnim(const char* filename, bool allocate, D11Rdh* rdh);
	ID3D11ShaderResourceView* _CreateTexture(D11Rdh* rdh, ID3D11Resource** resource);
};


//////////////////////////////////////////////////////////////////////////
// 오프 텍스쳐
class D11OffTexture : public D11Texture2, public qbOffTexture
{
	KCH_AUTOCLASS(D11OffTexture, D11Texture2, "D11OffTexture");
	KCH_FRIEND(D11Rdh);

private:
	ID3D11RenderTargetView*		_rtv;
	D11DepthBuffer*				_db;

	D11OffTexture*				_save_tex;
	kint						_save_cnt;

	bool						_lock_off;

private:
	D11OffTexture() throw() {};
	~D11OffTexture() throw();

public:
	K_OVR qbOffTexture* GetOff() { return this; }

	// qgITextureOff
	K_OVR bool Enter(kint clearance, const tpl::ColorValue& color, float depth, kuint stencil);
	K_OVR bool Leave();

private:
	bool _CreateOff(D11Rdh* rdh);
};


//////////////////////////////////////////////////////////////////////////
// 디바이스

// 스왑체인
class D11SwapChain : public tpl::MemAlloc<true>
{
	KCH_FRIEND(D11Rdh);

private:
	IDXGISwapChain*				_sc;
	ID3D11RenderTargetView*		_rtv;
	ID3D11DepthStencilView*		_dsv;
	ID3D11Texture2D*			_tex;
	D3D11_VIEWPORT				_vp;

public:
	D11SwapChain();
	~D11SwapChain();

	HRESULT Create(IDXGIFactory* factory, ID3D11Device* device, DXGI_SAMPLE_DESC aasd, tpl::Point size, HWND hwnd, bool windowed);
	HRESULT Layout(ID3D11Device* device, DXGI_SAMPLE_DESC aasd, tpl::Point size);
	void Dispose();

	//
	IDXGISwapChain* GetSc() { return _sc; }
	ID3D11RenderTargetView* GetRenderTargetView() { return _rtv; }
	ID3D11DepthStencilView* GetDepthStencilView() { return _dsv; }
	ID3D11Texture2D* GetTexture() { return _tex; }

	//
	bool Flush();
	void Render(ID3D11DeviceContext* context);
};

// 다이나 버퍼
class D11DynBuffer : public tpl::MemAlloc<true>
{
	KCH_FRIEND(D11Rdh);

public:
	enum { MAXBUFFER = 6 };

private:
	D11Buffer*					_ptr[MAXBUFFER];
	kint						_size[MAXBUFFER];
	kint						_index;

	qgBufferType				_type;
	D11Rdh*						_rdh;

public:
	D11DynBuffer(D11Rdh* rdh, qgBufferType type, kint sizes[MAXBUFFER]);
	~D11DynBuffer();

	//
	kpointer Enter(kint size);
	D11Buffer* Leave();
};

// 카파
struct D11Capabilities
{
	K_FORCEINLINE static kuint D11MaxTextureUnitCount() { return QGSGTEX_MAX_VALUE; }
	K_FORCEINLINE static kuint D11MaxOffTextureCount() { return QGSGOFF_MAX_VALUE; }
};

// 렌더러
class D11Rdh : public qgRdh, public D11Capabilities
{
	KCH_SUBCLASS(D11Rdh, qgRdh);
	KT_DECL_TYPE_INIT(D11Rdh);
	KT_DECL_TYPE_FINAL(D11Rdh);

public:
	// 상태값
	struct State
	{
		ID3D11DepthStencilState*	dhsl;
		ID3D11RasterizerState*		rasz;
		ID3D11BlendState*			blnd;
		ID3D11SamplerState*			splr[QGSGTEX_MAX_VALUE];

		ID3D11ShaderResourceView*	srvs[QGSGTEX_MAX_VALUE];
		ID3D11InputLayout*			ilos;
		ID3D11VertexShader*			shd_vs;
		ID3D11PixelShader*			shd_ps;
		D11Shader*					shd_ptr;

		ID3D11DepthStencilView*		dsv;
		D11OffTexture*				off_texs[QGSGOFF_MAX_VALUE];
		kint						off_cnt;

		qgTopology					tpg;
		ID3D11Buffer*				ib;
		ID3D11Buffer*				vb[QGSGVERT_MAX_VALUE];

		UINT						pup_tpg;
		UINT						pup_verts;
		UINT						pup_vertstride;
		UINT						pup_indices;
		UINT						pup_indstride;
		bool						pup_glyph;
		bool						pup_ortho;
	};

	// 상태값
	struct Extp
	{
		char						prof_vs[16];
		char						prof_ps[16];
		char						prof_gs[16];

		qgTexture*					tx_white;
		qgShader*					sp_ortho;
		qgShader*					sp_proj;
		qgShader*					sp_glyph;
		qgVertexLayout*				vl_p4_color;
		qgVertexLayout*				vl_p3_t2_color;
	};

	// 화면 정보
	struct Screen
	{
		tpl::vector<qbScreenDesc>	scr;
		tpl::vector<qbAdapterDesc>	adt;
	};

	// 보관
	struct Keep
	{
		kObjMukum					rdos;
		kObjMukum					shaders;
		kObjMukum					vertexlayouts;
		kObjMukum					depthbuffers;
		kObjMukum					texture2s;
		kObjMukum					texture3s;
		kObjMukum					animtextures;
		kObjList					buffers;
		bool						dispose;
	};

	// DLL
	typedef HRESULT(WINAPI *pD3DReflect)(LPCVOID pSrcData, SIZE_T SrcDataSize, REFIID pInterface, void** ppReflector);

	static PFN_D3D11_CREATE_DEVICE D3D11CREATEDEVICE;

	static pD3DCompile D3DCOMPILE;
	static pD3DReflect D3DREFLECT;

private:
	tpl::RefPtr<ID3D11Device>			_device;
	tpl::RefPtr<ID3D11DeviceContext>	_context;

	D3D_DRIVER_TYPE			_driver_type;
	D3D_FEATURE_LEVEL		_feature_level;
	DXGI_SAMPLE_DESC		_msaa;

	D11SwapChain*			_psc;
	D11DynBuffer*			_dib;
	D11DynBuffer*			_dvb;

	Screen					_scrs;

	Extp					_extp;
	State					_cache;

	Keep					_keep;

private:
	D11Rdh();
	~D11Rdh();

public:
	ID3D11Device* GetDevice() { return _device; }
	ID3D11DeviceContext* GetContext() { return _context; }

	D3D_DRIVER_TYPE GetDriverType() { return _driver_type; }
	D3D_FEATURE_LEVEL GetFeatureLevel() { return _feature_level; }
	DXGI_SAMPLE_DESC GetMsaa() { return _msaa; }

	const char* GetProfileVS() { return _extp.prof_vs; }
	const char* GetProfilePS() { return _extp.prof_ps; }
	const char* GetProfileGS() { return _extp.prof_gs; }

	void GetOffTexture(kint& cnt, D11OffTexture*& off) { cnt = _cache.off_cnt; off = _cache.off_texs[0]; }

public:
	K_OVR bool Layout(kint width, kint height);

	K_OVR bool Enter(kint clearance);
	K_OVR bool Leave();
	K_OVR bool Flush();

	K_OVR void ClearMRTs(kint clearance, kint colorcount, const tpl::ColorValue* colors, float depth, kuint stencil);

	K_OVR void SetViewport(kint x, kint y, kint w, kint h, float minz, float maxz);
	K_OVR void SetScissor(bool enable, const tpl::Rect& area);

	K_OVR void SetDhsl(qgDhsl* dhsl);
	K_OVR void SetRasz(qgRasz* rasz);
	K_OVR void SetBlnd(qgBlnd* blnd);
	K_OVR void SetSplr(kint stage, qgSplr* splr);

	K_OVR void SetShader(qgShader* shd, qgVertexLayout* lo);
	K_OVR void SetBufferVertex(kint stage, qgBuffer* buf);
	K_OVR void SetBufferIndex(qgBuffer* buf);

	K_OVR void SetTexture(kint stage, qgTexture* tex);
	K_OVR void SetRenderTargets(kint count, qgTexture** tex);

	K_OVR void Draw(qgTopology tpg, kint verts);
	K_OVR void DrawIndexed(qgTopology tpg, kint indices);
	
	K_OVR void DrawSprite(const tpl::Rect& bound, const tpl::ColorValue& color, qgTexture* tex, const tpl::Quad* coord);
	
	K_OVR void DrawLines(bool ortho, bool strip, kint count, const tpl::Vec3* pts, const tpl::ColorValue* colors);
	K_OVR void DrawStrips(bool ortho, kint count, const tpl::Vec3* pts, const tpl::ColorValue* colors, qgTexture* tex, const tpl::Vec2* coords);

	K_OVR void PrimitiveBegin(qgTopology tpg, kint verts, kint stride, kpointer* data);
	K_OVR void PrimitiveEnd();
	K_OVR void PrimitiveBeginIndexed(qgTopology tpg, kint verts, kint vertstride, kpointer* vertdata, kint indices, kint indstride, kpointer* inddata);
	K_OVR void PrimitiveEndIndexed();

	K_OVR void GlyphBegin(qgShader* shd);
	K_OVR void GlyphDraw(kint x, kint y, kint w, kint h, const tpl::ColorValue& color, qgTexture* tex);
	K_OVR void GlyphEnd();

	K_OVR qgRasz* CreateRasz(const qbRaszInitializer& inz);
	K_OVR qgBlnd* CreateBlnd(const qbBlndInitializer& inz);
	K_OVR qgSplr* CreateSplr(const qbSplrInitializer& inz);
	K_OVR qgDhsl* CreateDhsl(const qbDhslInitializer& inz);
	K_OVR qgShader* CreateShader(const char* name);
	K_OVR qgTexture* CreateTexture(const char* key);
	K_OVR qgTexture* CreateTexture2(const char* key, qgImage* img, kint flags);
	K_OVR qgTexture* CreateTexture2(qgColorFormat fmt, kuint width, kuint height, kuint mips, kint flags, const char* key);
	K_OVR qgTexture* CreateTexture3(qgColorFormat fmt, kuint width, kuint height, kuint depth, kuint mips, kint flags, const char* key);
	K_OVR qgTexture* CreateTextureCube(qgColorFormat fmt, kuint size, kuint mips, kint flags, const char* key);
	K_OVR qgTexture* CreateAnimTexture(const char* filename, bool allocate = false);
	K_OVR qgDepthBuffer* CreateDepthBuffer(kuint width, kuint height);
	K_OVR qgVertexLayout* CreateLayout(kint count, const qbLayoutElement* data);
	K_OVR qgBuffer* CreateBuffer(qgBufferType type, kint stride, kint size, kint flags, kconstpointer data);

protected:
	K_OVR bool _Initialize(qgStub* stub, kint flags);
	K_OVR bool _ResetPrepare(const tpl::Point& size, bool prepare);
	K_OVR void _Finalize();

private:
	bool _CommitShader() throw();
	void _InternalSetTopology(qgTopology tpg);
	void _InternalSetBufferVertex(kint stage, D11Buffer* buf, UINT stride, UINT offset, bool check);
	void _InternalSetBufferIndex(D11Buffer* buf, DXGI_FORMAT fmt, UINT offset, bool check);

	bool _EnumDisplayMode(IDXGIAdapter* adapter);
	void _RoamInit();

public:
	void _RoamRdoUnload(qgRdo* base);
	void _RoamLayoutUnload(qgRdo* base);
	void _RoamShaderUnload(qgRdo* base);
	void _RoamTexture2Unload(qgRdo* base);
	void _RoamTexture3Unload(qgRdo* base);
	void _RoamAnimTextureUnload(qgRdo* base);
	void _RoamBufferUnload(qgRdo* base);
	void _RoamDepthBufferUnload(qgRdo* base);
	qgRdo* _RoamShaderFind(const D11Shader::Key& key);
};

#endif	// __INC_QGDRV_D11_RDH_H__
